using System;
using System.Globalization;
using System.Reflection;

namespace UnitTesting.Fixturing
{
	class ReflectionValueProviderFor<T> : ReflectionValueProviderBase,
	                                      IConstructorSpecifier<T>,
	                                      IArgumentSpecifier
	{
		readonly IArgumentSpecifier _argumentSpecifier;
		ConstructorSelectorBase<T> _constructorSelector;

		internal ReflectionValueProviderFor(IValueProvider valueProvider, IFreezable freezable, object fixtureOptions)
			: this(new ArgumentService(Ensure.IsNotNull(() => valueProvider)), valueProvider, freezable, fixtureOptions)
		{
		}

		ReflectionValueProviderFor(ArgumentService argumentService, IValueProvider valueProvider, IFreezable freezable, object fixtureOptions)
			: this(new VerboseConstructorSelector<T>(false), argumentService, argumentService, valueProvider, freezable, fixtureOptions)
		{
		}

		internal ReflectionValueProviderFor(ConstructorSelectorBase<T> constructorSelector,
		                                    IArgumentSpecifier argumentSpecifier,
		                                    IArgumentProvider argumentProvider,
		                                    IValueProvider valueProvider,
		                                    IFreezable freezable,
		                                    object fixtureOptions)
			: base(argumentProvider, new CurrentActivator(), valueProvider, freezable, fixtureOptions)
		{
			_argumentSpecifier = argumentSpecifier;
			_constructorSelector = constructorSelector;
		}

		protected ReflectionValueProviderFor()
		{
			
		}

		public void WithConstructor(ConstructorSelectorBase<T> constructorSelector)
		{
			Ensure.IsNotNull(() => constructorSelector);
			_constructorSelector = constructorSelector;
		}

		protected internal override ConstructorInfo GetConstructor(Type fromType)
		{
			var constructor = _constructorSelector.Select();
			EnsureIsConstructorMatchedWith(constructor, fromType);
			return constructor;
		}

		static void EnsureIsConstructorMatchedWith(ConstructorInfo constructor, Type type)
		{
			if (constructor.DeclaringType != type)
			{
				throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "The type '{0}' of specified constructor is not matched with type '{1}'", constructor.DeclaringType, type));
			}
		}

		public void WithArgument(IArgument argument)
		{
			_argumentSpecifier.WithArgument(argument);
		}
	}
}